﻿using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;

namespace Buddy.Samples
{
    static partial class Program
    {
        static void Samples_Channel()
        {
            ThreadPool.SetMaxThreads(5000, 2500);
            ThreadPool.SetMinThreads(2000, 1000);

            Chan1();

            Chan2();

            Chan3();

            Chan4_timeout();

            Console.WriteLine("press any key to continue ...");
            Console.ReadKey();
        }

        static void Chan4_timeout()
        {
            var data = new Channel<int>();
            var timedout = new Channel<bool>();

            var timeout = new Task(() =>
            {
                bool b = false;
                Thread.Sleep(TimeSpan.FromSeconds(3));
                timedout.Close();
            });
            var reader = new Task(() =>
            {
                int loc = 0;
                data.Receive(out loc);
            });

            reader.Start();
            timeout.Start();

            //data.Send(13);

            var ti = Task.WaitAny(new Task[] { timeout, reader });

            switch (ti)
            {
                case 0:
                    Console.WriteLine("reader timed out");
                    break;
                case 1:
                    Console.WriteLine("reader done");
                    break;
            }
        }

        static void Chan3()
        {
            var c = new Channel<int>(100);
            long readN = 0;
            long writeN = 0;

            int writerN = 100;
            int readerN = 10;
            var writers = new List<Task>();
            var readers = new List<Task>();

            for (int i = 0; i < readerN; i++) readers.Add(new Task(() =>
            {
                int loc = 0;
                while (c.Receive(out loc)) { Interlocked.Increment(ref readN); }

                //Console.WriteLine("chan closed (reader)");
            }));
            for (int i = 0; i < writerN; i++)
            {
                writers.Add(new Task(() =>
                {
                    int loc = 0;
                    while (c.Send(loc++)) Interlocked.Increment(ref writeN);

                    //Console.WriteLine("chan closed (writer)");
                }));
            }

            foreach (var r in readers) r.Start();
            foreach (var w in writers) w.Start();

            var recNo = 0;
            do
            {
                Console.WriteLine("{0} {1} {2}", recNo++, Interlocked.Read(ref readN), Interlocked.Read(ref writeN));
                Thread.Sleep(TimeSpan.FromSeconds(1));
            } while (recNo < 3);

            c.Close();

            Console.WriteLine("{0} {1} {2}", recNo++, Interlocked.Read(ref readN), Interlocked.Read(ref writeN));
        }

        static void Chan2()
        {
            var c = new Channel<int>(100);
            long readN = 0;
            long writeN = 0;

            int msgN = 10000;
            int writerN = 100;
            int readerN = 10;
            var writers = new List<Task>();
            var readers = new List<Task>();

            for (int i = 0; i < readerN; i++) readers.Add(new Task(() =>
            {
                int loc = 0;
                while (c.Receive(out loc)) { Interlocked.Increment(ref readN); }

                Console.WriteLine("rcvd done or failed");
            }));
            for (int i = 0; i < writerN; i++)
            {
                writers.Add(new Task(() =>
                {
                    for (int j = 0; j < msgN; j++)
                    {
                        if (!c.Send(j))
                        {
                            Console.WriteLine("send failed");
                            return;
                        }
                        else
                        {
                            Interlocked.Increment(ref writeN);
                        }
                    }
                }));
            }

            foreach (var r in readers) r.Start();
            foreach (var w in writers) w.Start();

            var recNo = 0;
            do
            {
                Console.WriteLine("{0} {1} {2}", recNo++, Interlocked.Read(ref readN), Interlocked.Read(ref writeN));
                Thread.Sleep(TimeSpan.FromSeconds(1));
            } while (Interlocked.Read(ref readN) < writerN * msgN);
            Console.WriteLine("{0} {1} {2}", recNo++, Interlocked.Read(ref readN), Interlocked.Read(ref writeN));
        }

        static void Chan1()
        {
            var size = 100000;
            var c = new Channel<int>(size);

            for (int i = 0; i < 10000; i++)
            {
                c.Send(i);
                int loc = 0;
                c.Receive(out loc);
            }

            var time = new Stopwatch();

            time.Start();
            for (int i = 0; i < size; i++) c.Send(i);
            time.Stop();
            Console.WriteLine("push {0:0.000000}", time.Elapsed.TotalMilliseconds / size);
            time.Reset();

            time.Start();
            for (int i = 0; i < size; i++)
            {
                int loc = 0;
                c.Receive(out loc);
            }
            time.Stop();
            Console.WriteLine("pop {0:0.000000}", time.Elapsed.TotalMilliseconds / size);
            time.Reset();
        }
    }
}
